因為我們Node.js是使用了Express框架來進行開發,所以在進行實例開發前,我們得先了解Express的運作模式,那它又與原先Node.js的運作又有何不同呢?待筆者娓娓道來。
先前在Node JS-Backend見聞錄(00):關於本系列文章中有提到Node.js的運作模式。
圖片擷取自:Evan M. Hahn(2016). Express in action.
圖中的架構是有兩個部分所結合。左邊是主從式架構(Client-server model),右邊是Node.js的部分,其運作分為4個動作。依序為:
藉由上述的動作,Node.js就能來針對不同request的需求,來回覆相對應的response。而且,Node.js的HTTP的server也幫我們處理了Client端與JavaScript參數之間的關係。
舉個例子,若單純使用Node.js來開發個GET method的API,且回傳格式為application/json
,其內容為"say": "hi"
的話,寫法會是:
//引入node的http模組。
const http = require("http");
//定義個function來處理即將到來的http request.
requestHandler = (request, response) => {
// 回傳JSON格式的response訊息
const json = JSON.stringify({
say: 'hi'
});
response.end(json);
}
//使用http模組來建立一個server,並使用上述定義的function來處理request.
const server = http.createServer(requestHandler);
//啟動server並使用3000的port.
server.listen(3000);
但在Express中,整個運作模式就會變成:
圖片擷取自:Evan M. Hahn(2016). Express in action.
圖中的架構是有三個部分所結合。左邊是主從式架構(Client-server model),中間是Node.js的部分,右邊是Express框架部分,其運作分為5個動作。依序為:
與Node.js不同的是,Express有了下述的特色:
在Express的開發環境中,假設我們server端收到了一個需要知道這個request傳來的時間(request的時間function),且之後還要進行認證的功能(request的認證function)...等。待這些function處理完後才能發送response到client端。而中間的這些request所處理的function,我們都可以視為是middleware。
示意圖:
Client -> Request -> Server -> time function ------------|
|
Client <- Response <- Server <- authorization function <-|
Express提供了Router的功能,透過它我們能更簡便的去設定API的HTTP method及API URL。若單純使用Node.js來做開發,要做到這點用來控管HTTP method及API URL,則需要透過類似下列的判斷才行:
if (request.method === 'POST' && request.url === '/echo')
註記:該例子HTTP method為POST, API URL為/echo
但在Express開發環境中,我們可透過Express的Router內置模組,幫我們更簡便達到控管HTTP method及API URL。其範例文章後面的範例會提到。
這個特色可以與上一個特色「Router」有關,上述提到我們可以透過Router來協助我們控管HTTP method及API URL,但同時我們也能在單一指定的Router function中,來針對各個API來進行開發。並確保各個API所撰寫的程式碼不會互相影響,間接產生高耦合的問題。
這邊帶個情境來說明:
假設我們有三個API需要開發,分別是:
在單純只有Node.js的開發環境中會是:
requestHandler = (request, response) => {
if (request.method === 'GET' && request.url === '/test1') {
// do something
}
if (request.method === 'GET' && request.url === '/test2') {
// do something
}
if (request.method === 'POST' && request.url === '/test3') {
// do something
}
}
在Express開發環境中會是:
router.get('/test1', function(req, res) {
// do something
}
router.get('/test2', function(req, res) {
// do something
}
router.post('/test3', function(req, res) {
// do something
}
這樣讀者應該不難感受到個別的差異。
Express提供了許多自定義的便捷function,透過使用這些function我們可以更快的達到我們所想要做的事情。
舉個例子,假設我們想要回傳一個content type為application/json
的response給client端。
在Node.js開發環境中會是:
// 回傳JSON格式的response訊息
const json = JSON.stringify({
say: 'hi'
});
response.end(json);
在Express開發環境中會是:
res.json({
say: 'hi'
})
讀者應該會覺得根本就是語法糖。
Express提供了一個快速產生環境的指令,其設定開參考Express應用程式產生器。若要使用,我們可以在terminal中輸入:
// 安裝express產生器
$ npm install express-generator -g
// 生成express應用程式,並建立在`myapp`的資料夾中。
$ express --view=ejs myapp
註:
ejs
為template engine,是可直接透過後端來產出html文件的工具。
其資料結構為:
.
├── app.js
├── bin
│ └── www
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── index.js
│ └── users.js
└── views
├── error.ejs
└── index.ejs
之後再輸入npm install
指令,來安裝Express應用程式產生器幫我們設定好的套件:
$ npm install
這樣Express的開發環境就完成了。
相對的,若要使用Express的環境來開發上述所提到「開發個GET method的API,且回傳格式為application/json
,其內容為"say": "hi"
。」的話,我們僅需到routes資料夾中的index.js來進行開發即可,其寫法如下:
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.json({
say: 'hi'
})
});
module.exports = router;
註記:port部分的設定,Express應用程式產生器幫我們設置在bin資料夾的www中,有興趣的讀者可以研究看看。
同時,我們也可以透過app.js或自行生成一個資料夾或檔案來管理自定義的middleware部分。
之後,我們就可以透過指令npm start
來開啟整個專案。沒意外應該能在url為localhost:3000
中,看到"say": "hi"
的application/json
response。
同時,讀者也可從範例中看到在Express環境中,是如何做到控管HTTP method及API URL的方式。
最後,我們試著寫一個各個API被呼叫時,都能共用middleware看看。
首先,先進入到app.js檔案中,並加入:
app.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});
該middleware的功用是當有API被呼叫時,就顯示出被呼叫的時間。
待加入上述的middleware後,讀者可嘗試透過npm start
指令來開啟專案,來看看API被呼叫後,是否有印出呼叫API時的時間。
經上述的比較後,讀者應該能理解使用了Express框架後,對我們後端工程師要開發API的效率能提昇不少。但筆者撰寫這篇分享,最主要是希望我們工程師在使用框架語言來協助開發效率的同時,也要能理解我們所使用的框架,到底幫助了我們什麼,且其原理為何。在下篇分享中,將說明Express的MVC。
Evan M. Hahn(2016). Express in action.